Skip to content

Adds the core proxy engine#14

Merged
mwfj merged 59 commits intomainfrom
support-upstream-request-forwarding
Apr 12, 2026
Merged

Adds the core proxy engine#14
mwfj merged 59 commits intomainfrom
support-upstream-request-forwarding

Conversation

@mwfj
Copy link
Copy Markdown
Owner

@mwfj mwfj commented Apr 7, 2026

Summary

A proxy engine that:

  • receives client HTTP requests
  • selects an upstream via configured routes
  • forwards requests over pooled connections
  • parses upstream responses
  • relays them back to clients

What was built

Adds the core proxy engine: receive a client HTTP request, select an upstream service via configured route, forward the request over a pooled connection, parse the upstream HTTP/1.1 response, and relay it back to the client.

Builds on the connection pool infrastructure from #13.

  • 14 new files
  • 1,580 lines of production code
  • 1,719 lines of tests
  • 12 modified files
  • +385 lines in modified files

What’s new

Proxy engine (ProxyHandlerProxyTransaction)

  • Per-upstream ProxyHandler

    • Stateless factory created once at Start()
    • Registered as async routes via RouteAsync
  • Per-request ProxyTransaction

    • State machine:
      • INIT
      • CHECKOUT_PENDING
      • SENDING_REQUEST
      • AWAITING_RESPONSE
      • RECEIVING_BODY
      • COMPLETE
    • Orchestrates the full proxy lifecycle
    • Uses shared_from_this() self-capture for async safety
  • Config-driven auto-registration

    • Upstreams with a proxy section auto-register routes at startup
    • Manual HttpServer::Proxy() API is also available
  • HTTP/2 downstream transparent

    • Async handler API (AsyncCompletionCallback) handles H1/H2 delivery
    • ProxyTransaction is protocol-agnostic

Foundation components

  • UpstreamHttpCodec

    • llhttp in HTTP_RESPONSE mode
    • Explicit 1xx interim response handling (resume + discard loop)
    • 64MB body cap
    • Repeated header preservation via vector<pair<...>>
  • HttpRequestSerializer

    • Stateless HTTP/1.1 wire format serializer
    • Preserves query strings
  • HeaderRewriter

    • RFC 7230 §6.1 hop-by-hop header removal
    • X-Forwarded-For append
    • X-Forwarded-Proto
    • Via
    • Configurable Host rewrite
    • Strips headers listed in Connection
  • RetryPolicy

    • Idempotency-aware retry
    • Configurable retry conditions:
      • connect failure
      • 5xx
      • timeout
      • disconnect
    • Jittered exponential backoff

Infrastructure changes

  • HttpRequest

    • Added peer metadata fields:
      • client_ip
      • client_tls
      • client_fd
    • Populated on both H1 and H2 dispatch paths
  • HttpResponse

    • Added BadGateway() and GatewayTimeout() factory methods
  • PoolPartition::ReturnConnection

    • Added IsClosing() check
    • Destroys early-response-poisoned connections instead of returning them to idle
    • Prevents write buffer corruption
  • ProxyConfig

    • Full config schema:
      • route_prefix
      • strip_prefix
      • methods
      • response_timeout_ms
      • header_rewrite
      • retry
    • Parsing, validation, and serialization added in ConfigLoader

New files (14)

File Purpose
include/upstream/upstream_response.h Parsed upstream response struct (vector headers)
include/upstream/upstream_http_codec.h llhttp response-mode parser with 1xx handling
include/upstream/http_request_serializer.h HTTP/1.1 wire format serializer
include/upstream/header_rewriter.h Hop-by-hop removal, XFF/XFP/Via/Host rewriting
include/upstream/retry_policy.h Idempotency-aware retry with jittered backoff
include/upstream/proxy_transaction.h Per-request state machine
include/upstream/proxy_handler.h Per-upstream handler factory
server/upstream_http_codec.cc Response parser with 1xx interim handling
server/http_request_serializer.cc Request serializer
server/header_rewriter.cc Header rewriter
server/retry_policy.cc Retry policy
server/proxy_transaction.cc Transaction state machine (293 lines)
server/proxy_handler.cc Handler factory
test/proxy_test.h 50 tests (39 unit + 11 integration)

Modified files

File Change
include/config/server_config.h ProxyConfig structs
include/http/http_request.h Peer metadata (client_ip, client_tls, client_fd)
include/http/http_response.h BadGateway(), GatewayTimeout() factories
server/config_loader.cc Parse / validate / serialize proxy config
server/http_connection_handler.cc Peer metadata population (H1)
server/http2_session.cc Peer metadata population (H2)
server/http_server.cc Proxy(), RegisterProxyRoutes()
server/pool_partition.cc IsClosing() check in ReturnConnection
server/http_response.cc BadGateway() / GatewayTimeout() implementations
Makefile New source files, test_proxy target

Note: one summary lists 10 modified files, while the pasted implementation summary reports 12 modified files overall.


Key design decisions

Decision Rationale
Buffered response (v1) Existing complete(HttpResponse) API requires fully-formed responses. Streaming deferred to v2.
Request data copy at construction H1 async path calls parser_.Reset() immediately after handler returns, so storing const HttpRequest* would be a use-after-reset.
Early-response connection poison If upstream responds before request write completes, transport may have unsent bytes. MarkClosing() + pool IsClosing() check prevents corrupted connection reuse.
1xx transparent handling llhttp fires on_message_complete for 100 Continue. Codec loop detects status < 200, calls llhttp_resume(), resets interim response, and continues parsing.
weak_ptr in transport callbacks Prevents reference cycle: ProxyTransaction → lease → ConnectionHandler → callbacks → ProxyTransaction.
Precomputed static_prefix Strip-prefix path rewriting is parsed once in ProxyHandler constructor, not per request.

Configuration example

{
  "upstreams": [{
    "name": "user-service",
    "host": "10.0.1.10",
    "port": 8081,
    "pool": { "max_connections": 64 },
    "proxy": {
      "route_prefix": "/api/users",
      "strip_prefix": true,
      "methods": ["GET", "POST", "PUT", "DELETE"],
      "response_timeout_ms": 30000,
      "header_rewrite": {
        "set_x_forwarded_for": true,
        "rewrite_host": true
      },
      "retry": {
        "max_retries": 1,
        "retry_on_connect_failure": true
      }
    }
  }]
}

Error mapping

Upstream failure Client response
Connect failure / pool exhausted 502 Bad Gateway
Response timeout 504 Gateway Timeout
Upstream disconnect (incomplete response) 502 Bad Gateway
Malformed response (parse error) 502 Bad Gateway
5xx from upstream Forwarded as-is (retried if configured)

Test plan

50 new tests in test/proxy_test.h (./test_runner proxy / -P):

  • Unit tests (39)

    • UpstreamHttpCodec (11)
      • includes 1xx handling
      • repeated headers
      • 64MB cap
    • HttpRequestSerializer (5)
    • HeaderRewriter (8)
    • RetryPolicy (9)
    • ProxyConfig parsing (6)
  • Integration tests (11)

    • GET/POST proxy with real upstream servers
    • header rewriting
    • 502/504 error paths
    • strip_prefix
    • query string forwarding
    • connection reuse
    • early-response pool safety

Validation

  • All 302 tests pass
    • 252 existing
    • 50 new
  • Two consecutive runs stable

…m via configured routes, forwards requests over pooled connections, parses upstream responses, and relays them back to clients.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a comprehensive proxy engine for the HTTP server, adding support for asynchronous request forwarding, header rewriting, and configurable retry policies. The changes include a new upstream HTTP codec, request serialization, and logic to handle early upstream responses within the connection pool. Review feedback identifies a critical use-after-free vulnerability during route registration, a protocol violation concerning the Via header in responses, and several opportunities for code refactoring to reduce duplication. Additionally, it is recommended to integrate the existing backoff logic into the retry mechanism.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cb710daaf2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mwfj
Copy link
Copy Markdown
Owner Author

mwfj commented Apr 8, 2026

@codex review

@mwfj
Copy link
Copy Markdown
Owner Author

mwfj commented Apr 8, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new HTTP proxy engine, enabling the server to forward requests to upstream services with configurable header rewriting and retry policies. Key changes include new configuration structures for proxy behavior, the implementation of ProxyHandler and ProxyTransaction to manage proxy request lifecycles, and HeaderRewriter and RetryPolicy for request/response transformation and error handling. The HttpServer and HttpRequest classes were updated to support proxy routing and client metadata propagation. Comprehensive unit and integration tests for the new proxy functionality have also been added. Review feedback indicates that the proxy's retry mechanism currently lacks backoff, which could lead to a thundering herd problem, and suggests implementing a timer-based deferred retry. Additionally, a duplicated string literal in HeaderRewriter should be refactored into a constant for better maintainability.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc8f351c37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mwfj
Copy link
Copy Markdown
Owner Author

mwfj commented Apr 12, 2026

LGTM

@mwfj mwfj merged commit ac343af into main Apr 12, 2026
2 checks passed
@mwfj mwfj deleted the support-upstream-request-forwarding branch April 12, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant